/*  Copyright (C) 2008  Alexander Blyzniuchenko (BlezAlex)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using ICSharpCode.SharpZipLib.BZip2;

namespace DC
{
    [XmlInclude(typeof(DcDirectory))]
    public class FileListing : DcDirectory
    {
        private bool _isLoaded = false;
        private static readonly XmlSerializer _serializer = new XmlSerializer(typeof(FileListing));
        private readonly HubClient _client;
        private readonly object _lockObject = new object();

        // only by deserialization
        public FileListing()
        {
            _isLoaded = true;
        }

        internal FileListing(string name, bool isLoaded, HubClient client)
        :base(name)
        {
            _isLoaded = isLoaded;
            _client = client;
        }

        public static FileListing Load(Stream stream)
        {
            using (BZip2InputStream inputStream = new BZip2InputStream(stream))
            {
                return (FileListing)_serializer.Deserialize(inputStream);
            }
        }

        public void Save(Stream stream)
        {
            _serializer.Serialize(stream, this, new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName("", "") }));
        }

        public override DcDirectory this[string path]
        {
            get
            {
                CheckLoad();
                return base[path];
            }
        }

        [XmlElement("Directory")]
        public override List<DcDirectory> Directories
        {
            get
            {
                CheckLoad();
                return base.Directories;
            }
        }

        private void CheckLoad()
        {
            lock (_lockObject)
            {
                if (!_isLoaded)
                {
                    try
                    {
                        FileListing newList = _client.DownloadManager.GetFileList(Name);
                        _directories = newList.Directories;
                        Files = newList.Files;
                    }
                    catch (TimeoutException ex)
                    {
                        Trace.WriteLine(Name + ex.Message);
                    }
                    catch(IOException ex)
                    {
                        Trace.WriteLine(Name + ex.Message);
                    }
                }
                _isLoaded = true;
            }
        }
    }

    [XmlRoot("Directory")]
    public class DcDirectory
    {
        public const string PATH_SEPARATOR = "/";

        [XmlAttribute]
        public string Name;

        protected List<DcDirectory> _directories = new List<DcDirectory>();

        [XmlElement("File")]
        public List<DcFile> Files = new List<DcFile>();

        /// <summary>
        /// Should be used only in deserialization
        /// </summary>
        public DcDirectory()
        {
        }

        public DcDirectory(string name)
        {
            Name = name.Replace(":", "_");
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            if (typeof(DcDirectory) != obj.GetType())
                return false;

            return Name == ((DcDirectory)obj).Name;
        }

        public override int GetHashCode()
        {
            return Name.GetHashCode();
        }

        public virtual DcDirectory this[string path]
        {
            get
            {
                if (path.StartsWith(PATH_SEPARATOR))
                    path = path.Substring(1);

                if (path.Length == 0)
                    return this;

                if (path.Contains(PATH_SEPARATOR))
                {
                    string directoryName = path.Substring(0, path.IndexOf(PATH_SEPARATOR));
                    string newPath = path.Substring(path.IndexOf(PATH_SEPARATOR) + 1);
                    return GetDirectoryByName(directoryName)[newPath];
                }
                else
                {
                    return GetDirectoryByName(path);
                }
            }
        }

        [XmlElement("Directory")]
        public virtual List<DcDirectory> Directories
        {
            get { return _directories; }
        }

        protected DcDirectory GetDirectoryByName(string name)
        {
            return _directories.Find((delegate(DcDirectory value) { return value.Name == name; }));
        }
    }

    [XmlRoot("File")]
    public struct DcFile
    {
        [XmlAttribute]
        public string Name;

        [XmlAttribute]
        public long Size;

        [XmlAttribute]
        public string TTH;
    }
}
